package mdp;

import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

import main.Main;
import mdp.Action.Consequence;
import env.City;
import env.Road;
import env.World;

public class ValueIteration
{
	public class RoadsStateGenerator
	{
		private int[]	roadStates;
		public boolean	done	= false;

		public RoadsStateGenerator(World world)
		{
			roadStates = new int[world.roads.size()];
			for (int i = 0; i < roadStates.length; i++)
			{
				roadStates[i] = -1;
			}
		}

		public void add()
		{
			for (int i = 0; i < roadStates.length; i++)
			{
				if (roadStates[i] != 1)
				{
					roadStates[i] += 1;
					return;
				} else
				{
					if (i == roadStates.length - 1)
					{
						done = true;
					}
					roadStates[i] = -1;
				}
			}
		}

		public void generate(Vector<State> states, World origWorld,
				String agentCityName, String chemCityName, String armyCityName)
		{
			while (!done)
			{
				World copyWorld = new World(origWorld);
				for (int i = 0; i < copyWorld.roads.size(); i++)
				{
					copyWorld.roads.elementAt(i).blocked = roadStates[i];
				}
				State newState = new State(
						copyWorld.getCityByName(agentCityName),
						copyWorld.getCityByName(chemCityName),
						copyWorld.getCityByName(armyCityName), copyWorld);
				newState.init();
				states.add(newState);
				add();
			}
		}
	}

	public World			origWorld;
	public Vector<State>	states;
	private Random			rand	= new Random(System.currentTimeMillis());

	public ValueIteration(World world)
	{
		this.origWorld = world;
		this.states = new Vector<State>();
	}

	public void init()
	{
		Iterator<City> agentCityIt = origWorld.cities.iterator();
		while (agentCityIt.hasNext())
		{
			City agentCity = (City) agentCityIt.next();
			Iterator<City> chemCityIt = origWorld.cities.iterator();
			while (chemCityIt.hasNext())
			{
				City chemCity = (City) chemCityIt.next();
				Iterator<City> armyCityIt = origWorld.cities.iterator();
				while (armyCityIt.hasNext())
				{
					City armyCity = (City) armyCityIt.next();
					RoadsStateGenerator r = new RoadsStateGenerator(origWorld);
					r.generate(states, origWorld, agentCity.name,
							chemCity.name, armyCity.name);
				}
			}

		}

		System.out.println("total number of states: "
				+ states.size()+"\n");
		generateActions();
//		System.out.println("finished generating actions and connecting all");
	}

	/***
	 * generate actions for all initial states.
	 */
	private void generateActions()
	{
		Iterator<State> sIt = states.iterator();
		while (sIt.hasNext())
		{
			State state = (State) sIt.next();
			if (state.initial)
				state.generateActions();
		}
	}

	public void printStates()
	{
		Iterator<State> s = states.iterator();
		while (s.hasNext())
		{
			State state = (State) s.next();
			printState(state);

		}
	}

	private void printState(State state)
	{
		if (state.reachable)
			System.out.println(state.output());
		else
			System.err.println(state.output());
	}

	public void printInitialStates()
	{
		Iterator<State> s = states.iterator();
		while (s.hasNext())
		{
			State state = (State) s.next();
			if (state.initial)
			{
				printState(state);
			}
		}
	}

	public void printFinalStates()
	{
		Iterator<State> s = states.iterator();
		while (s.hasNext())
		{
			State state = (State) s.next();
			if (state.finalState)
			{
				printState(state);
			}
		}
	}

	public State findState(City agent, City chem, City army, int[] roadsState)
	{
		Iterator<State> sIt = states.iterator();
		while (sIt.hasNext())
		{
			State state = (State) sIt.next();
			if (state.agent.name.equals(agent.name)
					&& state.chem.name.equals(chem.name)
					&& state.army.name.equals(army.name)
					&& state.equalRoadState(roadsState))
			{
				return state;
			}
		}
		return null;
	}

	public void calculate()
	{
		Double maxDelta = Main.delta + 1, deltaBefore = 0.0, deltaAfter = 0.0;
		int iterations = 0;
		while (maxDelta > Main.delta)
		//		for (int i = 0; i < 1000; i++)
		{
			iterations++;
			maxDelta = 0.0;
			Iterator<State> sIt = states.iterator();
			while (sIt.hasNext())
			{
				State s = (State) sIt.next();
				if (s.reachable)
				{
					deltaBefore = s.utility;
					s.calculateUtility();
					deltaAfter = s.utility;
					maxDelta = Math.max(Math.abs(deltaAfter - deltaBefore),
							maxDelta);
				}
			}
		}
//		System.out.println("iterations: " + iterations);
	}

	public State bestAction(State s)
	{
		if (!s.reachable)
		{
			System.out.println("unreachable state");
			return null;
		}
		if (s.agent.name.equals(Main.goalCity)
				&& s.chem.name.equals(Main.goalCity))
		{
			// goal reached
			return null;
		}
		double range = 0.0, lottery = rand.nextDouble();
		Iterator<Consequence> cIt = s.bestAction.consequences.iterator();
		while (cIt.hasNext())
		{
			Action.Consequence c = (Action.Consequence) cIt.next();
			range += c.prob;
			if (lottery < range)
			{
				return c.state;
			}
		}
		return null;//not supposed to happen
	}

	public void simulate()
	{
		int[] initialStateRoads = new int[origWorld.roads.size()];
		int i = 0;
		Iterator<Road> rIt = origWorld.roads.iterator();
		while (rIt.hasNext())
		{
			Road r = (Road) rIt.next();
			if (r.nearCity(origWorld.getCityByName(Main.initialAgentCity)))
			{
				double lottery = rand.nextDouble();
				if (lottery < r.tChance)
				{
					initialStateRoads[i] = 1;
				} else
				{
					initialStateRoads[i] = 0;
				}
			} else
			{
				initialStateRoads[i] = -1;
			}
			i++;
		}
		State s = findState(origWorld.getCityByName(Main.initialAgentCity),
				origWorld.getCityByName(Main.initialChemCity),
				origWorld.getCityByName(Main.initialArmyCity),
				initialStateRoads);
		while (s != null)
		{
			System.out.println(s.output());
			s = bestAction(s);
		}
	}

}
